namespace OX.Strongbind
{
    using System;
    using System.Windows.Forms;
    using OX.Strongbind.Dynamic;
    using OX.Strongbind.Remoting;    

    /// <summary>
    /// Represents a scope in which bindings can be
    /// declared.
    /// </summary>
    public class BindingScope : IBindingScope
    {
        private BindingScopeOptions options;
        private ProxyManager proxyManager;
        private bool disposed;

        /// <summary>
        /// Initializes a new binding scope in which
        /// bindings can be declared.
        /// </summary>
        public BindingScope()
            : this(new BindingScopeOptions())
        {
        }

        /// <summary>
        /// Initializes a new binding scope in which bindings can be declared, using the specified options for the scope.
        /// </summary>
        /// <param name="options">Configurable options for the binding scope.</param>
        public BindingScope(BindingScopeOptions options)
        {
            this.options = options;
            proxyManager = new ProxyManager(options.UseSeparateAppDomain, options.AllowProxiesInCurrentAppDomain);
            disposed = false;
        }

        /// <summary>
        /// Creates a new binding source, i.e. an object
        /// that can be used as the source for bindings.
        /// </summary>
        /// <typeparam name="T">The type of the binding source. Using an interface is recommended. 
        /// If the type is a concrete class containing non-virtual properties, the class should 
        /// inherit <see cref="MarshalByRefObject" />.</typeparam>
        /// <param name="instance">The instance to use as the binding source. 
        /// Using an instance that is declared as an interface type is recommended.
        /// If the declared type is a concrete class containing non-virtual properties, the class should 
        /// inherit <see cref="MarshalByRefObject" />.</param>
        /// <returns>A binding source ready for binding using <see cref="Bind"/>.</returns>
        public T CreateSource<T>(T instance)
        {
            if (IsDisposed)
                throw new InvalidOperationException("Binding scope is disposed");
            return proxyManager.CreateProxy(instance);
        }

        /// <summary>
        /// Creates a new binding target. A binding target can be used as the target for bindings.
        /// The target is usually a Windows Forms control, but can be any <see cref="IBindableComponent" />.
        /// </summary>
        /// <typeparam name="T">The type of the binding target. This must be a <see cref="IBindableComponent" />.</typeparam>
        /// <param name="instance">The instance to use as the binding target.</param>
        /// <returns>A binding target ready for use with <see cref="Bind"/>.</returns>
        public T CreateTarget<T>(T instance) where T : IBindableComponent, new()
        {
            if (IsDisposed)
                throw new InvalidOperationException("Binding scope is disposed");
            instance.DataBindings.Clear();
            return proxyManager.CreateProxy(instance);
        }

        /// <summary>
        /// Initializes a new binding from the given property on the binding source.
        /// </summary>
        /// <param name="propertyValue">The source property for the binding.</param>
        public PartialBinding Bind(object propertyValue)
        {
            if (IsDisposed)
                throw new InvalidOperationException("Binding scope is disposed");
            return new PartialBinding(proxyManager);
        }

        /// <summary>
        /// Gets the current binding pair. This method allows you to use Strongbind as a pure
        /// interceptor. Create proxies using Strongbind and then grab binding pairs using this
        /// method after properties on the proxies have been accessed.
        /// </summary>
        /// <returns></returns>
        public BindingPair GetBindingPair()
        {
            return proxyManager.GetBindingPair();
        }

        /// <summary>
        /// Disposes the binding scope. Must be called to free
        /// up resources used during binding declaration.
        /// </summary>
        public void Dispose()
        {
            proxyManager.Dispose();
            disposed = true;
        }

        /// <summary>
        /// Tells whether or not the binding scope is disposed.
        /// </summary>
        public bool IsDisposed
        {
            get { return disposed; }
        }
    }
}
